package com.itdct.onflow.generator.base

import cn.hutool.core.lang.UUID
import com.itdct.onflow.core.extend.logWarn
import com.itdct.onflow.core.extend.timeStampToFormatDate
import com.itdct.onflow.generator.config.GeneratorConfig
import com.itdct.onflow.generator.constant.GroupEnum
import com.itdct.onflow.generator.model.ClassModel
import com.itdct.onflow.generator.model.CompareResultModel
import com.itdct.onflow.generator.model.FieldModel

/**
 * @author Zhouwx
 * @date 2025/8/11 10:26:57
 * @version 1.0
 * @description
 */
abstract class BaseTemplate(val classModel: ClassModel, val generatorConfig: GeneratorConfig) {
    /**
     * 模板渲染，获取最终结果
     */
    abstract fun render(): String

    /**
     * 当类或者字段发生变化时，与模板相关的代码应该如何更新
     */
    abstract fun onModify(compareResultModel: CompareResultModel, content: String): String

    /**
     * 模板所属分组
     */
    abstract fun belongGroup(): GroupEnum

    /**
     * 模板所属子模块名称
     */
    abstract fun subModuleName(): String

    /**
     * 最终输出名称
     */
    abstract fun getFolderFileName(): String

    /**
     * 需要过滤的列信息
     */
    val filterFieldList = HashSet<String>()

    /**
     * 通过字段生成代码
     * @param currentContent 当前文件中的总内容，如果是首次生成，传入空字符串即可。如果是增量生成，则需要传入当前文件中的所有内容，用于防止重复代码生成
     * @param getFieldLine 字段生成方法
     */
    fun fieldsGen(currentContent: String, getFieldLine: (Int, FieldModel) -> String): String {
        return fieldsGen(currentContent, classModel.fieldList, getFieldLine)
    }

    /**
     * 通过字段生成代码
     * @param currentContent 当前文件中的总内容，如果是首次生成，传入空字符串即可。如果是增量生成，则需要传入当前文件中的所有内容，用于防止重复代码生成
     * @param fieldList 字段列表
     * @param getFieldLine 字段生成方法
     * @param separator 两个字段内容之间的分隔符，可能就是换行，也可能是逗号+换行
     */
    fun fieldsGen(currentContent: String, fieldList: List<FieldModel>, getFieldLine: (Int, FieldModel) -> String, separator: String = "\n"): String {
        val lines = ArrayList<String>()
        fieldList.filter {
            !filterFieldList.contains(it.name)
        }.forEachIndexed { index, fieldModel ->
            val fieldLine = getFieldLine(index, fieldModel)
            if (!currentContent.contains(fieldLine) && fieldLine.isNotBlank()) {
                lines.add(fieldLine)
            }
        }
        return lines.joinToString(separator)
    }

    fun getDateString(): String {
        return System.currentTimeMillis().timeStampToFormatDate()
    }

    fun uuidPrint(): String {
        val folderFileName = getFolderFileName()
        val suffix = folderFileName.substring(folderFileName.lastIndexOf(".") + 1).lowercase()
        val uuid = UUID.fastUUID().toString(true)
        when (suffix) {
            "xml" -> {
                return """<!-- [[ OnflowGenerator File UUID: $uuid  ]] -->"""
            }

            else -> {
                return """// [[ OnflowGenerator File UUID: $uuid  ]]"""
            }
        }
    }

    fun rangeStart(rangeName: String): String {
        return rangePrint(rangeName, "Start")
    }

    fun rangeEnd(rangeName: String): String {
        return rangePrint(rangeName, "End")
    }

    protected fun rangePrint(rangeName: String, step: String): String {
        val folderFileName = getFolderFileName()
        val suffix = folderFileName.substring(folderFileName.lastIndexOf(".") + 1).lowercase()
        when (suffix) {
            "xml" -> {
                return """<!-- [[ OnflowGenerator Range (- $rangeName -) ${step} ]] -->"""
            }

            else -> {
                return """// [[ OnflowGenerator Range (- $rangeName -) ${step} ]]"""
            }
        }
    }

    fun insertData(content: String, splitIndex: Int, addContent: String): String {
        val head = content.substring(0, splitIndex)
        val tail = content.substring(splitIndex)
        return head + addContent + tail
    }

    fun insertFieldListContent(
        totalContent: String,
        fieldModels: ArrayList<FieldModel>,
        splitIndex: Int,
        onFieldContent: (index: Int, fieldModel: FieldModel) -> String,
        separator: String = "\n",
        prefix: String = "",
        suffix: String = ""
    ): String {
        var content = totalContent
        if (splitIndex == -1) {
            return content
        }
        if (fieldModels.isNotEmpty()) {
            val head = totalContent.substring(0, splitIndex)
            val tail = totalContent.substring(splitIndex)

            val fieldsGen = fieldsGen(totalContent, fieldModels, { i, fieldModel ->
                onFieldContent(i, fieldModel)
            }, separator)

            content = head + prefix + "\n" + fieldsGen + suffix + "\n" + tail
        }
        return content
    }

    fun insertFieldListContent(
        totalContent: String,
        fieldModels: ArrayList<FieldModel>,
        rangeName: String,
        onFieldContent: (index: Int, fieldModel: FieldModel) -> String,
        separator: String = "\n",
        prefix: String = "",
        suffix: String = ""
    ): String {
        var content = totalContent

        val rangeStartString = rangeStart(rangeName)
        val rangeEndString = rangeEnd(rangeName)

        val startIndex = content.indexOf(rangeStartString)
        val endIndex = content.indexOf(rangeEndString)

        if (startIndex == -1 || endIndex == -1) {
            logWarn("未找到指定范围：$rangeName")
            return content
        }

        if (fieldModels.isEmpty()) {
            return content
        }

        val head = content.substring(0, startIndex)
        val tail = content.substring(endIndex)

        val rangeContent = content.substring(startIndex, endIndex)

        val fieldsGen = fieldsGen(rangeContent, fieldModels, { i, fieldModel ->
            onFieldContent(i, fieldModel)
        }, separator)

        content = head + prefix + rangeContent + fieldsGen + suffix + "\n" + tail

        return content
    }

    fun deleteRangeFunctionCode(
        content: String,
        rangeName: String,
        delFieldModels: ArrayList<FieldModel>,
        functionContent: (fieldModel: FieldModel) -> String,
        afterReplace: ((scope: String) -> String)? = null
    ): String {
        var content = content
        if (delFieldModels.isEmpty()) {
            return content
        }

        val startIndex = content.indexOf(rangeStart(rangeName))
        val endIndex = content.indexOf(rangeEnd(rangeName))
        if (startIndex == -1 || endIndex == -1) {
            return content
        }
        var scope = content.substring(startIndex, endIndex)
        val head = content.substring(0, startIndex)
        val tail = content.substring(endIndex)

        for (fieldModel in delFieldModels) {
            val replaceContent = functionContent(fieldModel).trim()
            scope = scope.replace(replaceContent, "")
        }
        scope = afterReplace?.invoke(scope) ?: scope
        content = head + scope + tail
        return content
    }

    fun modifyReplace(content: String, rangeName: String, modifyPairs: ArrayList<Pair<FieldModel, FieldModel>>, fieldContent: (fieldModel: FieldModel) -> String): String {
        var content = content

        val startIndex = content.indexOf(rangeStart(rangeName))
        val endIndex = content.indexOf(rangeEnd(rangeName))
        if (startIndex == -1 || endIndex == -1) {
            return content
        }
        var scope = content.substring(startIndex, endIndex)
        val head = content.substring(0, startIndex)
        val tail = content.substring(endIndex)

        for ((originFieldModel, newFieldModel) in modifyPairs) {
            val originFieldContent = fieldContent(originFieldModel)
            val newFieldContent = fieldContent(newFieldModel)
            if (originFieldContent != newFieldContent && !content.contains(newFieldContent)) {
                scope = scope.replace(originFieldContent, newFieldContent)
            }
        }

        content = head + scope + tail
        return content
    }
}